package cc.iotkit.jql;

import cn.hutool.core.io.IoUtil;
import jdk.nashorn.api.scripting.NashornScriptEngine;
import lombok.extern.slf4j.Slf4j;

import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * javascript sql读取器
 */
@Slf4j
public class ScriptSqlReader {

    private final NashornScriptEngine engine = (NashornScriptEngine) (new ScriptEngineManager()).getEngineByName("nashorn");
    private final Object scriptObj;
    private final Pattern pattern = Pattern.compile("`([^`]+)`", Pattern.MULTILINE);

    public ScriptSqlReader(Class<?> interfaceCls) throws ScriptException {
        InputStream inputStream = interfaceCls.getResourceAsStream(interfaceCls.getSimpleName() + ".js");
        String script = IoUtil.readUtf8(inputStream);
        //将方法改为成员方法
        script = script.replaceAll("function\\s+(\\w+)\\s*\\(", "this.$1=function(");
        //处理脚本中的``
        Matcher matcher = pattern.matcher(script);
        while (matcher.find()) {
            String sql = matcher.group(1);
            String newSql = sql.replaceAll("([^\\n]+)\\n", "$1\\\\n");
            //替换${}变量拼接，将${xx}变成"+xx+"
            newSql = newSql.replaceAll("\\$\\{([^\\}]+)\\}", "\"+($1)+\"");
            script = script.replace(String.format("`%s`", sql), String.format("\"%s\"", newSql));
            script = script.replaceAll("\"\\s*\\n+\\s*(\\w+)", "\"$1");
        }

        scriptObj = engine.eval(String.format("new (function () {\n%s})()", script));
    }

    public String getSql(Method method, Object[] args, Map<String, Object> params) throws ScriptException, NoSuchMethodException {
        String sql = "";
        String name = method.getName();
        Object result = engine.invokeMethod(scriptObj, name, args);
        if (result != null) {
            sql = result.toString();
        }
        StringBuilder sbParam = new StringBuilder();
        params.forEach((key, val) -> sbParam.append(key).append("=").append(val).append(","));

        log.info("get {} sql:\n{},\nparams:{}", name, sql, sbParam.toString());
        return sql;
    }
}
